package com.sakiko.audiobook.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sakiko.audiobook.album.TrackInfoFeignClient;
import com.sakiko.audiobook.common.constant.RedisConstant;
import com.sakiko.audiobook.common.constant.SystemConstant;
import com.sakiko.audiobook.common.rabbit.constant.MqConst;
import com.sakiko.audiobook.common.rabbit.service.RabbitService;
import com.sakiko.audiobook.common.result.Result;
import com.sakiko.audiobook.model.album.TrackInfo;
import com.sakiko.audiobook.model.user.*;
import com.sakiko.audiobook.user.factory.StrategyFactory;
import com.sakiko.audiobook.user.mapper.*;
import com.sakiko.audiobook.user.service.UserInfoService;
import com.sakiko.audiobook.user.strategy.ItemTypeStrategy;
import com.sakiko.audiobook.vo.user.UserInfoVo;
import com.sakiko.audiobook.vo.user.UserPaidRecordVo;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.joda.time.LocalDateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;

    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;

    @Autowired
    private StrategyFactory strategyFactory;

    /**
     * 微信登陆
     *
     * @param code
     * @return
     */
    @Override
    public Map<String, Object> wxLogin(String code) {
        Map<String, Object> map = new HashMap<>();
        // 获取 openid (微信用户的唯一标识)
        WxMaJscode2SessionResult wxMaJscode2SessionResult = null;
        try {
            wxMaJscode2SessionResult = wxMaService.jsCode2SessionInfo(code);
        } catch (WxErrorException e) {
            throw new RuntimeException(e);
        }
        String openid = wxMaJscode2SessionResult.getOpenid();
        // 根据 openid，获取用户信息
        UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getWxOpenId, openid));
        // 判断用户是否存在
        if (null == userInfo) {
            // 用户未注册，进行注册，初始化账户金额
            userInfo = new UserInfo();
            userInfo.setWxOpenId(openid);
            userInfo.setNickname("听友:" + UUID.randomUUID().toString().substring(0, 8));
            userInfo.setAvatarUrl("http://192.168.6.101:9000/spirit-melody-musings/9a14ea5dedca4f4688d89be1964a2a20.png");
            // 保存数据
            this.save(userInfo);
            // 初始化账户金额 openFeignClient
            // 异步 rabbitMq 发送消息内容: userId 给谁充钱
            rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, userInfo.getId());
        }
        // 声明一个 token
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        // 将用户信息存储到缓存
        String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
        redisTemplate.opsForValue().set(loginKey, userInfo, RedisConstant.USER_LOGIN_REFRESH_KEY_TIMEOUT, TimeUnit.SECONDS);
        // 存储数据
        map.put("token", token);
        return map;
    }

    /**
     * 根据用户 id，获取用户信息
     *
     * @param userId
     * @return
     */
    @Override
    public UserInfoVo getUserInfo(Long userId) {
        // 获取用户信息
        UserInfo userInfo = this.getById(userId);
        // 属性拷贝
        UserInfoVo userInfoVo = new UserInfoVo();
        BeanUtils.copyProperties(userInfo, userInfoVo);
        // 返回数据
        return userInfoVo;
    }

    /**
     * 更新用户信息
     *
     * @param userInfoVo
     * @param userId
     */
    @Override
    public void updateUser(UserInfoVo userInfoVo, Long userId) {
        // 更新数据
        UserInfo userInfo = this.getById(userId);
        userInfoVo.setId(userId);
        BeanUtils.copyProperties(userInfoVo, userInfo);
        this.updateById(userInfo);
    }

    @Override
    public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> trackIdList) {
        if (trackIdList == null || trackIdList.isEmpty())
            return Collections.emptyMap(); // 过滤掉免费集数后可能是空的，返回一个空的 map而不是 null，因为远程微服务的 Assert.notNull判断

        // 判断用户是否购买过专辑 map.put(trackId, 0|1) ==> select * from user_paid_album where user_id = ? and album_id = ?  (指定了某张单一专辑 selectOne)
        UserPaidAlbum userPaidAlbum = userPaidAlbumMapper.selectOne(new LambdaQueryWrapper<UserPaidAlbum>().eq(UserPaidAlbum::getUserId, userId).eq(UserPaidAlbum::getAlbumId, albumId));
        if (null != userPaidAlbum) { // 购买了整张专辑
//            trackIdList.forEach(trackId -> map.put(trackId, 1)); // 开销小，但有状态 (这个需要外面 new Map)
//            trackIdList.stream().collect(Collectors.toMap(trackId -> trackId, value -> 1, (oldValue, newValue) -> newValue)); // 存在 stream流开销 (list -> map 这里的 k v 都是 list的 e)
            return trackIdList.stream().collect(Collectors.toMap(Function.identity(), v -> 1, (oldValue, newValue) -> newValue));
        }

        // 当前用户没有购买过这个专辑，但是购买过部分声音;
        // 先初始化一个长度为 trackIdList.size的 map把默认值置为 0
        Map<Long, Integer> map = trackIdList.stream().collect(Collectors.toMap(Function.identity(), v -> 0, (oldValue, newValue) -> newValue));
        // 查询用户购买当前专辑的声音 Id列表有哪些 ==> select * from user_paid_track where user_id = 17 and album_id = 391 and track_id in (...) # 最后一个条件的存在是因为有可能 vip免费等条件不考虑专辑的某些声音，只处理其他微服务调用需要的trackIdList即可
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(new LambdaQueryWrapper<UserPaidTrack>().eq(UserPaidTrack::getUserId, userId)
                .eq(UserPaidTrack::getAlbumId, albumId)
                .in(UserPaidTrack::getTrackId, trackIdList));
        // 把购买记录的 id记录一下
        Set<Long> paidTrackIds = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toSet());
        // 更新源 map
        paidTrackIds.forEach(paidTrackId -> map.put(paidTrackId, 1));
        return map;
    }

    @Override
    public Boolean isPaidAlbum(Long albumId, Long userId) {
        return null != userPaidAlbumMapper.selectOne(new LambdaQueryWrapper<UserPaidAlbum>().eq(UserPaidAlbum::getUserId, userId).eq(UserPaidAlbum::getAlbumId, albumId));
    }

    @Override
    public List<Long> findUserPaidTrackList(Long albumId, Long userId) {
        return userPaidTrackMapper.selectList(new LambdaQueryWrapper<UserPaidTrack>().eq(UserPaidTrack::getUserId, userId).eq(UserPaidTrack::getAlbumId, albumId)).stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
        // 记录用户购买的信息 (策略+工厂模式实现)
        // 1、从策略工厂中获取对应的策略对象
        ItemTypeStrategy strategy = strategyFactory.getStrategy(userPaidRecordVo.getItemType());
        // 2、执行策略对象的任务
        strategy.savePaidRecord(userPaidRecordVo);
        //        if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(userPaidRecordVo.getItemType())) {
//            // 购买专辑 (判断专辑对象是否存在)
//            UserPaidAlbum userPaidAlbum = userPaidAlbumMapper.selectOne(new LambdaQueryWrapper<UserPaidAlbum>().eq(UserPaidAlbum::getUserId, userPaidRecordVo.getUserId())
//                    .eq(UserPaidAlbum::getAlbumId, userPaidRecordVo.getItemIdList().get(0)));
//            if (null != userPaidAlbum) return; // 购买过
//            // 购买专辑
//            userPaidAlbum = new UserPaidAlbum();
//            userPaidAlbum.setUserId(userPaidRecordVo.getUserId());
//            userPaidAlbum.setAlbumId(userPaidRecordVo.getItemIdList().get(0));
//            userPaidAlbum.setOrderNo(userPaidRecordVo.getOrderNo());
//            // 保存记录
//            userPaidAlbumMapper.insert(userPaidAlbum);
//        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(userPaidRecordVo.getItemType())) {
//            // 购买声音
//            List<Long> trackIdList = userPaidRecordVo.getItemIdList();
////            UserPaidTrack userPaidTrack = userPaidTrackMapper.selectById(trackIdList.get(0)); 不可能存在要插入的记录 这里绝对的错误 ❌
//            Result<TrackInfo> trackInfoResult = trackInfoFeignClient.getTrackInfo(trackIdList.get(0));
//            Assert.notNull(trackInfoResult, "声音信息对象结果集不存在");
//            TrackInfo trackInfo = trackInfoResult.getData();
//            Assert.notNull(trackInfo, "声音信息对象不存在");
//            // 查询用户购买过的声音
//            List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(new LambdaQueryWrapper<UserPaidTrack>().eq(UserPaidTrack::getUserId, userPaidRecordVo.getUserId())
//                    .eq(UserPaidTrack::getAlbumId, trackInfo.getAlbumId()));
//            List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
//            // 重复付款则退款 (一般推荐购买和回显的时候都正常的话不会发生，UI上没有付款标识)
//            userPaidRecordVo.getItemIdList().forEach(trackId -> {
//                if (!CollectionUtils.isEmpty(userPaidTrackList)) {
//                    // 退款(重复支付的)
//                    if (userPaidTrackIdList.contains(trackId)) {
//                        // 发送退款消息;  trackId, userId, amount,消费 receiver 调用相关 api增加账户金额
////                        rabbitService.sendMessage(MqConst.EXCHANGE_CANCEL_ORDER, MqConst.ROUTING_CANCEL_ORDER, OneEntity);
//                    }
//                }
//                // 其他正常保存记录
//                UserPaidTrack userPaidTrack = new UserPaidTrack();
//                userPaidTrack.setUserId(userPaidRecordVo.getUserId());
//                userPaidTrack.setAlbumId(trackInfo.getAlbumId());
//                userPaidTrack.setTrackId(trackId);
//                userPaidTrack.setOrderNo(userPaidRecordVo.getOrderNo());
//                userPaidTrackMapper.insert(userPaidTrack);
//            });
//        } else {
//            // 购买 VIP
//            UserVipService userVipService = new UserVipService();
//            userVipService.setUserId(userPaidRecordVo.getUserId());
//            userVipService.setOrderNo(userPaidRecordVo.getOrderNo());
//            userVipService.setStartTime(new Date());
//            // 设置过期时间
//            // case 1: 用户购买 user_info.is_vip = 0; case 2: 用户续期
//            UserInfo userInfo = this.getById(userPaidRecordVo.getUserId());
//            // 获取用户 vip购买时长
//            Long vipServiceConfigId = userPaidRecordVo.getItemIdList().get(0);
//            VipServiceConfig vipServiceConfig = vipServiceConfigMapper.selectById(vipServiceConfigId);
//            Integer serviceMonth = vipServiceConfig.getServiceMonth();
//            // 创建系统时间
//            Date newExpireTime = new Date();
//            if (1 == userInfo.getIsVip() && userInfo.getVipExpireTime().after(new Date())) {
//                // 是 vip 续期
//                newExpireTime = new LocalDateTime(userInfo.getVipExpireTime()).plusMonths(serviceMonth).toDate();
//            } else {
//                // 不是 vip 购买
//                newExpireTime = new LocalDateTime(newExpireTime).plusMonths(serviceMonth).toDate();
//            }
//            userVipService.setExpireTime(newExpireTime);
//            userVipServiceMapper.insert(userVipService);
//            // 考虑用户第一次买 设置 user_info.is_vip = 1;
//            userInfo.setIsVip(1);
//            userInfo.setVipExpireTime(newExpireTime);
//            this.updateById(userInfo);
//        }
    }
}
